package com.drore.app;

import android.content.Context;
import android.text.TextUtils;
import android.util.Log;

import com.drore.app.utils.ACache;
import com.drore.app.utils.CacheType;
import com.drore.app.utils.CrashHandler;
import com.drore.app.utils.EventType;
import com.drore.app.utils.LogUtils;
import com.drore.app.utils.LogcatFileManager;
import com.drore.app.utils.PhoneInfoUtil;
import com.drore.app.utils.StringUtils;
import com.drore.app.utils.TimeLogUtils;
import com.drore.app.utils.UpLoadUtils;
import com.drore.app.utils.UploadStatus;
import com.drore.www.logbeansecret.bean.CommitBean;
import com.drore.www.logbeansecret.bean.EventBean;
import com.drore.www.logbeansecret.bean.NetConnectBean;
import com.drore.www.logbeansecret.bean.PhoneBean;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * log的配置入口
 *
 * @author sixbing
 * @date 2018/11/8 11:39
 */

public class LogRecordUtil {
    private static final long DAYS = 24 * 60 * 60 * 1000; //天
    private static final long M = 1024 * 1024; //M
    private static final long DEFAULT_DAY_WEEK = 7 * DAYS; //默认删除天数
    private static final long DEFAULT_FILE_SIZE = 10 * M;
    private static final long DEFAULT_MIN_SDCARD_SIZE = 50 * M; //最小的SD卡小于这个大小不写入
    private static final int DEFAULT_COUNT = 50;
    private static boolean showLog = true;
    private static Context mContext = null;
    private static String TAG = "LogRecordUtil";
    private static Builder mBuilder;//构建工具
    private static Map<String, Long> longMap = new HashMap<>();//存储页面停留事件的map
    //    private static DBDao dbDao;//数据库操作类
    private static ACache mAcahe;

    private static int MAX_TRY_TIMES = 3;//上传失败尝试次数
    private static int UPLOAD_ERROR_TIMES = 0;//已经上传失败的次数
    private static UploadStatus uploadStatus = UploadStatus.NONE;//上传的状态


    /**
     * 初始化日志
     *
     * @param context context
     * @param builder builder
     */
    public static void initLog(Context context, Builder builder) {
        mBuilder = builder;
        mContext = context;
        LogUtils.openLog = showLog;
        /**
         * ACache
         */
        mAcahe = ACache.get(mContext);
        if (mBuilder.mEncryptPrivateKey == null || context == null || mBuilder.appCrashFilePath == null) {
            LogUtils.e(TAG, " builder 参数部分为空！！！初始化失败。请检查参数。");
            LogUtils.e(TAG, " 参数。mBuilder.mEncryptprivateKey:" + mBuilder.mEncryptPrivateKey
                    + "---context" + context + "---mBuilder.appCrashPath" + mBuilder.appCrashFilePath);
            return;
        }
        /**
         * 开启崩溃拦截
         */
        CrashHandler.getInstance().init(mContext, mBuilder.appCrashFilePath, mBuilder.mDay, true);
        //增加启动日志
        appLaunch();
        //开启logcat e 日志记录
        openLogcatRecord();
        LogUtils.e(TAG, "LogRecordUtil-->CrashHandler  initLog success ");
    }

    /**
     * 开启 logcat e 日志记录
     * 在application onCreate() 中开启
     */
    public static void openLogcatRecord() {
        if (mBuilder.mAppLogcatFilePath == null) {
            LogUtils.e(TAG, " builder 参数 mAppLogcatFilePath 为空！！！开启 logcat 日志保存文件失败。请检查参数。");
            return;
        }
        LogcatFileManager.getInstance().start(mBuilder.mAppLogcatFilePath);
    }

    /**
     * 开启 log e 打印，发布模式建议关闭
     * public static void initLog(Context context, Builder builder, boolean debug)
     * debug  isOpen  showLog
     */
    public static void openLog(boolean isOpen) {
        showLog = isOpen;
    }

    /**
     * 关闭 logcat  日志记录
     * 在application  onTerminate（） 中关闭
     */
    public static void stopLogcatRecord() {
        LogcatFileManager.getInstance().stop();
    }

    /**
     * 发送上传log CommitBean
     */
    public static synchronized void sendLogs() {
        if (uploadStatus == UploadStatus.UPLOADING) {
            LogUtils.e(TAG, " 已经在上传中。。。");
            return;
        }
        if (uploadStatus == UploadStatus.STOPUPLOAD) {
            LogUtils.e(TAG, "本次启动不允许上传，由于上传三次失败。");
            return;
        }
        if (TextUtils.isEmpty(mBuilder.upLoadUrl)) {
            LogUtils.e(TAG, " upLoadUrl is  null");
            return;
        }
        if (TextUtils.isEmpty(mBuilder.mEncryptPrivateKey)) {
            LogUtils.e(TAG, " mEncryptPrivateKey is  null");
            return;
        }
        CommitBean commitBean = new CommitBean();
        commitBean.setEventBeanList(getEventBeanList());
        commitBean.setNetConnectBeanList(getNetSatauBeanList());
        commitBean.setPhoneBeanList(getPhoneBeanList());
        sendLogRightNow(mBuilder.upLoadUrl, commitBean);
    }

    /**
     * 达到上传的条件，立即上传
     *
     * @param commitBean 上传的内容
     */
    private static void sendLogRightNow(final String upLoadUrlfinal, final CommitBean commitBean) {
        try {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    UPLOAD_ERROR_TIMES++;
                    LogUtils.e(TAG, "sendLogRightNow 开始第" + UPLOAD_ERROR_TIMES + "次上传......");
                    uploadStatus = UploadStatus.UPLOADING;
                    String result = UpLoadUtils.doJsonPost(upLoadUrlfinal, new Gson().toJson(commitBean), mBuilder.mEncryptPrivateKey);
                    //判断是否上传成功
                    if (result.contains("200")) {
                        LogUtils.e(TAG, "上传成功：" + result);
                        uploadSuccess();
                    } else {
                        uploadError(upLoadUrlfinal, commitBean);
                    }

                }
            }).start();
        } catch (Exception e) {
            LogUtils.e(TAG, " 上传失败！");
            uploadError(upLoadUrlfinal, commitBean);
            e.printStackTrace();
        }
    }

    /**
     * 上传所有日志失败
     */
    private static void uploadError(final String upLoadUrlfinal, final CommitBean commitBean) {
        if (UPLOAD_ERROR_TIMES < MAX_TRY_TIMES) {
            uploadStatus = UploadStatus.ERROR;
            sendLogRightNow(upLoadUrlfinal, commitBean);
        } else {
            LogUtils.e(TAG, "上传所有日志失败:" + MAX_TRY_TIMES + "次，检查接口去吧！");
            uploadStatus = UploadStatus.STOPUPLOAD;
        }
    }

    /**
     * 上传所有日志成功
     */
    private static void uploadSuccess() {
        UPLOAD_ERROR_TIMES = 0;
        uploadStatus = UploadStatus.SUCCESS;
        deleteAllEventAndPhoneInfo();
    }

    /**
     * 设置网络请求事件（包含请求的各个参数）
     * netConnectBean.isSuccess()  true  false
     * 1、错误请求会立即上传服务器
     * 2、正确的请求会先存储在本地数据库中--》自动检测数据库的数据条数，达到限定值时全部上传。
     *
     * @param netConnectBean netConnectBean
     * @param isErrorUpload  是否在符合条件的前提下立即上传
     */
    public static void setNetDetail(final NetConnectBean netConnectBean, boolean isErrorUpload) {
        if (netConnectBean == null) {
            Log.e(TAG, " NetConnectBean netConnectBean is  null ! ! !");
            return;
        }
        //下面这三个参数是初始化的时候获得的
        netConnectBean.setAppId(mBuilder.appId);
        netConnectBean.setAppVersion(mBuilder.appVersion);
        netConnectBean.setUdId(mBuilder.udid);
        try {
            putDataToAcache(netConnectBean, CacheType.NET_CONNECT_INFO);
            if (netConnectBean.isSuccess()) {//是否请求成功
                canAutoUpdate();
            } else if (isErrorUpload) {//是否在符合条件的前提下立即上传
                sendLogs();
            }
        } catch (Exception e) {
            e.printStackTrace();
            setBugEvent(e.toString());
        }
    }

    /**
     * 存入文件
     *
     * @param obj EventBean NetConnectBean PhoneBean
     * @param key CacheType 中的一个
     */
    public static void putDataToAcache(Object obj, String key) {
        String str = "";
        if (obj instanceof EventBean) {
            EventBean eventBean = (EventBean) obj;
            //先获得列表，然后添加一个数据，在存储全部的数据。注意
            List<EventBean> eventBeanList = getEventBeanList();
            eventBeanList.add(eventBean);
            str = new Gson().toJson(eventBeanList);
        } else if (obj instanceof NetConnectBean) {
            NetConnectBean netConnectBean = (NetConnectBean) obj;
            //先获得列表，然后添加一个数据，在存储全部的数据。注意
            List<NetConnectBean> connectBeanList = getNetSatauBeanList();
            connectBeanList.add(netConnectBean);
            str = new Gson().toJson(connectBeanList);
        } else if (obj instanceof PhoneBean) {
            PhoneBean phoneBean = (PhoneBean) obj;
            //每次都設置新的，只有一個数据 注意
            List<PhoneBean> phoneBeanList = new ArrayList<>();
            phoneBeanList.add(phoneBean);
            str = new Gson().toJson(phoneBeanList);
        } else {
            str = new Gson().toJson(new ArrayList<>());
        }
        mAcahe.put(key, str, mAcahe.TIME_MONTH);

    }

    /**
     * 是否可以自动上传
     */
    private static void canAutoUpdate() {
        List<NetConnectBean> netConnectBeanList = getNetSatauBeanList();
        List<EventBean> eventBeanList = getEventBeanList();
        int allCount = getEventBeanList().size() + getNetSatauBeanList().size();
        if (allCount > mBuilder.mMaxCount) {
            Log.e(TAG, "all event count is :" + allCount + " bigger than " + mBuilder.mMaxCount);
            sendLogs();
        } else {
            for (NetConnectBean netConnectBean : netConnectBeanList) {
                if (netConnectBean != null && !netConnectBean.isSuccess()) {
                    sendLogs();
                    return;
                }
            }
            for (EventBean eventBean : eventBeanList) {
                if (eventBean != null && eventBean.getEventtype().equals("" + EventType.BUG_EVENT)) {
                    sendLogs();
                    return;
                }
            }
        }
    }

    /**
     * 应用启动调用，存储手机信息，方便bug修改
     */
    public static void appLaunch() {
        PhoneBean phoneBean = PhoneInfoUtil.getPhoneBean(mContext);
        phoneBean.setAppid(mBuilder.appId);
        try {
            putDataToAcache(phoneBean, CacheType.PHONE_INFO);
            LogUtils.e(TAG, "APP启动");
        } catch (Exception e) {
            e.printStackTrace();
            setBugEvent(e.toString());
        }

    }

    /**
     * 启动事件存储
     */
    public static void setAppLaunchEvent() {
        setStartStayTime("appStay");
    }

    /**
     * 退出应用事件
     */
    public static void setAppStopEvent() {
        if (!TextUtils.isEmpty(mBuilder.udid)) {
            setEndStayTime("appStay", EventType.APP_LAUNCH);
        }
    }

    /**
     * 点击事件存储
     */
    public static void setClickEvent(String pageId) {
        String time = TimeLogUtils.getCurrentTimeInString();
        EventBean eventBean = new EventBean("" + EventType.CLICK_EVENT, time, time,
                "0", "点击事件", pageId, mBuilder.appVersion, mBuilder.appId, mBuilder.udid);
        try {
            putDataToAcache(eventBean, CacheType.EVENT_INFO);
            canAutoUpdate();
        } catch (Exception e) {
            e.printStackTrace();
            setBugEvent(e.toString());
        }
    }

    /**
     * 页面停留开始，以页面的id为键把开始的时间存储在map中
     *
     * @param pageId 页面id
     */
    public static void setStartStayTime(String pageId) {
        if (TextUtils.isEmpty(pageId)) {
            LogUtils.e(TAG, "setStartStayTime pageId is  null !");
            return;
        }
        longMap.put(pageId, System.currentTimeMillis());
    }

    /**
     * 页面离开的事件，计算开始，结束的时间差
     *
     * @param pageId 页面id
     */
    public static void setEndStayTime(String pageId) {
        setEndStayTime(pageId, EventType.STAY_TIME);
    }

    public static void setEndStayTime(String pageId, int eventType) {
        if (TextUtils.isEmpty(pageId)) {
            LogUtils.e(TAG, "setEndStayTime pageId is  null !");
            return;
        }
        if (StringUtils.isNull(longMap.get(pageId))) {
            LogUtils.e(TAG, "longMap.get(pageId) is null !");
            return;
        }
        long startTime = longMap.get(pageId);
        long endTime = System.currentTimeMillis();
        if (startTime > 0) {
            long duration = endTime - startTime;
            String detail = "其他事件";
            longMap.remove(pageId);
            if (eventType == EventType.APP_LAUNCH) {
                detail = "app启动信息";
            } else if (eventType == EventType.STAY_TIME) {
                detail = "页面停留";
            }
            EventBean eventBean = new EventBean("" + eventType, TimeLogUtils.getTime(startTime), TimeLogUtils.getTime(endTime),
                    "" + (duration / 1000), detail, pageId, mBuilder.appVersion, mBuilder.appId, mBuilder.udid);
            try {
                putDataToAcache(eventBean, CacheType.EVENT_INFO);
                canAutoUpdate();
            } catch (Exception e) {
                e.printStackTrace();
                setBugEvent(e.toString());
            }
        } else {
            longMap.remove(pageId);
        }
    }

    /**
     * 设置bug事件
     *
     * @param desc bug内容
     */
    public static void setBugEvent(String desc) {
        String time = TimeLogUtils.getCurrentTimeInString();
        EventBean eventBean = new EventBean("" + EventType.BUG_EVENT, time, time,
                "" + 0, "BugEvent", "", mBuilder.appVersion, mBuilder.appId, mBuilder.udid);
        eventBean.setEventdetail(desc);
        putDataToAcache(eventBean, CacheType.EVENT_INFO);
        canAutoUpdate();
    }

    /**
     * 获得网络请求的信息数量
     *
     * @return 数量
     */
    public static List<PhoneBean> getPhoneBeanList() {
        String str = mAcahe.getAsString(CacheType.PHONE_INFO);
        if (str == null || "true".equals(str) || "false".equals("str")) {
            return new ArrayList<>();
        }
        return new Gson().fromJson(str, new TypeToken<ArrayList<PhoneBean>>() {
        }.getType());
    }


    /**
     * 获得网络请求的信息数量
     *
     * @return 数量
     */
    public static List<NetConnectBean> getNetSatauBeanList() {
        String str = mAcahe.getAsString(CacheType.NET_CONNECT_INFO);
        if (str == null || "true".equals(str) || "false".equals("str")) {
            return new ArrayList<>();
        }
        return new Gson().fromJson(str, new TypeToken<ArrayList<NetConnectBean>>() {
        }.getType());
    }

    /**
     * 获得事件的信息数量
     *
     * @return 数量
     */
    public static List<EventBean> getEventBeanList() {
        String str = mAcahe.getAsString(CacheType.EVENT_INFO);
        if (str == null || "true".equals(str) || "false".equals("str")) {
            return new ArrayList<>();
        }
        List<EventBean> list = new Gson().fromJson(str, new TypeToken<ArrayList<EventBean>>() {
        }.getType());
        return list;
    }


    /**
     * 删除数据库所有的数据（不包括手机信息因为手机信息可以更新的）
     */
    public static boolean deleteAllEventAndPhoneInfo() {
        putDataToAcache(null, CacheType.EVENT_INFO);
//        putDataToAcache(new ArrayList<>(),CacheType.PHONE_INFO);
        putDataToAcache(null, CacheType.NET_CONNECT_INFO);
        return true;
//        boolean b1 = dbDao.deleteAll(new EventBean());
////        boolean b2 = dbDao.deleteAll(new PhoneBean());
//        boolean b3 = dbDao.deleteAll(new NetConnectBean());
//        if (b1 && b3) {
//            return true;
//        }
//        return false;
    }

    public static final class Builder {
        /**
         * 上传文件的url：http://oss.drore.com/uplaodfile?para1=1
         */
        private String upLoadUrl;
        /**
         * APP版本
         */
        private String udid;
        /**
         * APP名称
         */
        private String appVersion;
        /**
         * APP唯一id，在上传平台获取提供
         */
        private String appId;
        /**
         * LOGCAT  日志路径
         */
        private String mAppLogcatFilePath;
        /**
         * 奔溃日志位置 .TXT文件(奔溃日志存储在两个位置1、logan中type=1。2、TXT文件中)
         */
        private String appCrashFilePath;
        /**
         * 奔溃日志位置 .TXT文件
         */
        private long mMaxFile = DEFAULT_FILE_SIZE; //删除文件最大值
        private long mDay = DEFAULT_DAY_WEEK; //删除天数
        private String mEncryptPrivateKey; //私钥
        private String mEncryptPublicKey; //公钥
        private long mMinSDCard = DEFAULT_MIN_SDCARD_SIZE;
        private long mMaxCount = DEFAULT_COUNT; //数据库记录数大于此值时自动上传

        public Builder() {
        }

        public Builder setLogcatFilePath(String appLogcatFilePath) {
            this.mAppLogcatFilePath = appLogcatFilePath;
            return this;
        }

        @Deprecated
        public Builder setMaxFile(long maxFile) {
            this.mMaxFile = maxFile * M;
            return this;
        }

        public Builder setFileSaveDay(int day) {
            this.mDay = day * DAYS;
            return this;
        }

        public Builder setEncryptPrivateKey(String encryptPrivateKey) {
            this.mEncryptPrivateKey = encryptPrivateKey;
            return this;
        }

//        @Deprecated
//        public Builder setEncryptPublicKey(String encryptPublicKey) {
//            this.mEncryptPublicKey = encryptPublicKey;
//            return this;
//        }

        @Deprecated
        public Builder setMinSDCard(long minSDCard) {
            this.mMinSDCard = minSDCard;
            return this;
        }

        public final Builder setUpLoadUrl(String upLoadUrl) {
            this.upLoadUrl = upLoadUrl;
            return this;
        }

        public final Builder setAppId(String appId) {
            this.appId = appId;
            return this;
        }

        public final Builder setCurrentVersionCode(String udid) {
            this.udid = udid;
            return this;
        }

        public final Builder setAppCrashPath(String appCrashPath) {
            this.appCrashFilePath = appCrashPath;
            return this;
        }

        public final Builder setAppVersion(String appVersion) {
            this.appVersion = appVersion;
            return this;
        }

        public final Builder setMaxAutoUploadCount(int maxCount) {
            this.mMaxCount = maxCount;
            return this;
        }

        public final Builder build() {
            return this;
        }
    }

}
